package jfuzzyprincepless.zarchive.negotiation.protocols.utilityfuncprotocol;

import java.util.*;

import jfuzzyprincepless.zarchive.negotiation.IAgentsExporter;
import jfuzzyprincepless.negotiation.*;
import jfuzzyprincepless.negotiation.impl.BaseNegotiation;
import jfuzzyprincepless.agents.INegotiatingAgent;
import jfuzzyprincepless.zarchive.agents.IUtilityFuncNegotiationAgent;

/**
 * decorator for {@link #subnegotiation}
 */
public class UtilityFuncNegotiation
	extends BaseNegotiation
	implements IUtilityFuncNegotiation, IAgentsExporter
{
	private static final int DEFAULT_NEGOTIATION_LIMIT = 10;

	private INegotiation subnegotiation;

	private int negotiateLimit = DEFAULT_NEGOTIATION_LIMIT;

	private List<Object> criticalParams = new ArrayList<Object>();

	public UtilityFuncNegotiation(
		INegotiation subnegotiation
	) {
		this.subnegotiation = subnegotiation;
	}

	@Override
	public void negotiate(INegotiationContext context) {
		context.registerService(this);

		List<INegotiatingAgent> agents = getAgents();

		NegotiationUtils.onNegotiationStart(agents, context);

		for (int index = 0; index < negotiateLimit; index++) {
			subnegotiation.negotiate(context);
			onSingleNegotiationStageEnd(context, agents);
			analiseUtilities(context, agents);

			// TODOLONG: isOk - strategy, limit strategy
			boolean isOk = false;
			if (isOk) {
				break;
			}
		}
	}

	@Override
	public void execute(IExecutionContext context) {
		subnegotiation.execute(context);
	}

	@Override
	public List<INegotiatingAgent> getAgents() {
		return ((IAgentsExporter)subnegotiation).getAgents();
	}

	@Override
	public List<Object> getCriticalParams() {
		return criticalParams;
	}

	public void setNegotiateLimit(int negotiateLimit) {
		this.negotiateLimit = negotiateLimit;
	}

	private void analiseUtilities(
		INegotiationContext context,
		List<INegotiatingAgent> agents
	) {
		criticalParams.clear();

		IUtilityFuncNegotiationAgent minUtilAgent = null;
		double minUtility = Double.MAX_VALUE;
		for (INegotiatingAgent iagent : agents) {
			if (!(iagent instanceof IUtilityFuncNegotiationAgent)) {
				continue;
			}
			IUtilityFuncNegotiationAgent agent =
				(IUtilityFuncNegotiationAgent) iagent;
			// TODOLONG: different strategies of actual critical params selection
			double utility = agent.computeUtility(context);
			if (utility < minUtility) {
				minUtility = utility;
				minUtilAgent = agent;
			}
		}

		if (minUtilAgent == null) {
			return;
		}

		minUtilAgent.addCriticalParams(criticalParams);
	}

	private void onSingleNegotiationStageEnd(
		INegotiationContext context,
		List<INegotiatingAgent> agents
	) {
		for (INegotiatingAgent iagent : agents) {
			if (!(iagent instanceof IUtilityFuncNegotiationAgent)) {
				continue;
			}
			IUtilityFuncNegotiationAgent agent =
				(IUtilityFuncNegotiationAgent)iagent;
			agent.onSingleNegotiationStageEnd(context);
		}
	}
}
